<?php

/**
 * MessageController
 * 
 * @author
 * @version 
 */

require_once 'Zend/Controller/Action.php';

class MessageController extends Zend_Controller_Action {
	private $user;
	private $book;
	private $messageType;
	private $userMessage;
	private $message;
	private $mailQueue;
	private $messageThread;
	private $pageModel;
	
	public function init() {
	   $this->user = new User();
	   $this->book = new Book();
	   $this->messageType = new MessageType();
	   $this->userMessage = new UserMessage();
	   $this->message = new Message();
	   $this->mailQueue = new MailQueue();
	   $this->messageThread = new MessageThread();
	   $this->pageModel = new MessagePageModel();
	   
		$this->_flashMessenger = $this->_helper->getHelper('FlashMessenger');
		$this->initView();
		 $this->view->flashmsgs = $this->_flashMessenger->getMessages();
	}
	
	/**
	 * The default action - show the home page
	 */
	public function indexAction() {
	}
	
	/**
	 * Send message to user.
	 * Sender must login first.
	 */
	public function sendmessageAction() {
		if ($this->_helper->user->isLoggedIn()) {
			$targetUserId = $this->_getParam("uid");
			if (empty($targetUserId)) {
				throw new Zend_Exception("Target user is not specified!");
			}
			$senderUser = $this->user->findById($this->_helper->user->getUserData()->id);
			$targetUser = $this->user->findById($targetUserId);
			$sendMessageForm = new SendMessageForm(
			    $this->messageType->findByKey(MessageType::PRIVATE_MESSAGE)->id, 
			    $targetUser, 
			    $this->_getParam("id"));
			$this->view->form = $sendMessageForm;
			
			if ($this->getRequest()->isPost()) {
	            if ($sendMessageForm->isValid($_POST)) {
	            	$targetUserId = $sendMessageForm->getValue("targetUserId");
	            	$type = $sendMessageForm->getValue("messageType");
	            	$subject = trim($sendMessageForm->getValue("subject"));
	            	$message = nl2br(trim($sendMessageForm->getValue("message")));
	            	
	            	$bookISBN = $sendMessageForm->getValue("bookISBN");
	            	$db = Zend_Registry::get("db");
	            	$db->beginTransaction();
	            	$book = $this->book->findByISBN($bookISBN);
	            	$data = array(
	            	    Message::USER_ID => $this->_helper->user->getUserData()->id,
	            	    Message::MESSAGE => $message,
	            	    Message::TYPE => $type,
	            	    Message::CREATED => $this->_helper->generator->generateCurrentTime()
	            	);
	            	$messageId = $this->message->addMessage($data);
	            	$data = array(
	            	    UserMessage::MESSAGE_ID => $messageId,
	            	    UserMessage::TARGET_USER_ID => $targetUserId,
	            	);
	            	$userMessageId = $this->userMessage->addUserMessage($data);
	            	
	            	// add to message thread
	            	$newThreadId = $this->generateThreadId();
	            	$data = array(
	            	    MessageThread::THREAD_ID => $newThreadId,
	            	    MessageThread::SUBJECT => $subject,
	            	    MessageThread::USER_MESSAGE_ID => $userMessageId
	            	);
	            	$this->messageThread->addToThread($data);
	            	// log activity
	            	Activity::logActivity(
	            	  Activity::SEND_USER_MESSAGE, 
	            	  $senderUser->id,
	            	  $userMessageId,
	            	  $this->_helper->generator->generateCurrentTime()
	            	);
                    // add to mail queue
	            	$extra = array(
	            	    "thread" => $newThreadId,
	            	    "message" => $message,
	            	    "book" => $book
	            	);
	            	
	            	$emailMessage = EmailTemplate::getEmailMessage(Activity::SEND_USER_MESSAGE, $senderUser, $targetUser, $extra);
	            	$this->mailQueue->addToQueue(
	            	  Activity::SEND_USER_MESSAGE, 
	            	  EmailTemplate::SUBJECT_USER_MESSAGE, 
//	            	  $senderUser->email,
                      Constant::SYS_NO_REPLY_EMAIL,  
	            	  $targetUser->email, 
	            	  $emailMessage, 
	            	  $this->_helper->generator->generateCurrentTime());
	            	$db->commit();
	            	$this->_flashMessenger->addMessage("Your message is sent successfully!");
	            	
	            	if (empty($bookISBN)) {
	            		$this->_redirect("/user/profile/uid/".$targetUserId);
	            	} else {
	            		$this->_redirect("/book/detail/id/".$bookISBN);
	            	}
	            }
			}
		} else {
			$this->_redirect("/user/login?next=".urlencode($this->_helper->generator->getCurrentURI()));
		}
	}

    /**
     * Message inbox.
     *
     */
    public function inboxAction() {
        if ($this->_helper->user->isLoggedIn ()) {
            $user = $this->user->findById($this->_helper->user->getUserData()->id);
            
            $messageThreads = MessageThread::findAllMessageThreadSendToUser($user);
            $inboxMessages = $this->generateMessageThreadDTO($messageThreads, $user, "inbox");
            
            $this->view->user = $user;
            $this->view->paginator = $this->_helper->paginator( $this->_getParam ( 'page', 1 ), $inboxMessages );
			$this->view->pageTitle= $user->name."'s Inbox";
        } else {
            $this->_redirect( "/user/login?next=".urlencode($this->_helper->generator->getCurrentURI()) );
        }
    }
    
    /**
     * Message sent box.
     *
     */
    public function sentAction() {
        if ($this->_helper->user->isLoggedIn ()) {
        	$user = $this->user->findById($this->_helper->user->getUserData()->id);
        	
        	$messageThreads = MessageThread::findAllMessageThreadSendByUser($user);
            $sentMessages = $this->generateMessageThreadDTO($messageThreads, $user, "sent");           
            $this->view->user = $user;
            
            $this->view->paginator = $this->_helper->paginator( $this->_getParam ( 'page', 1 ), $sentMessages );
            $this->view->pageTitle= $user->name."'s Sent";
        } else {
            $this->_redirect( "/user/login?next=".urlencode($this->_helper->generator->getCurrentURI()) );
        }
    }
    
    /**
     * View single message thread action. given root message id.
     */
    public function viewAction() {
        if ($this->_helper->user->isLoggedIn ()) {
            $loggedInUser = $this->user->findById($this->_helper->user->getUserData()->id);
            $threadId = $this->_getParam("tid");
            $thread = $this->messageThread->findByThreadId($threadId);
            $this->getThreadUsers($thread);
            $messageThreadDTO = $this->generateSingleMessageThreadDTO($thread, $loggedInUser, null, true);
            
            if (isset($messageThreadDTO->userMessages)) {
            	// update all message send to loggedInUser to 'read' status
            	$db = Zend_Registry::get("db");
            	$db->beginTransaction();
            	foreach ($messageThreadDTO->userMessages as $userMessage) {
            		UserMessage::updateUserMessagesStatus($userMessage->userMessageId, $userMessage->creatorId, $userMessage->targeUserId, $loggedInUser);
            	}
            	$db->commit();
            	
                $this->view->thread = $messageThreadDTO;
                $form = new ReplyMessageForm($thread);
                $this->view->form = $form;
                
                $this->view->pageTitle=  $messageThreadDTO->subject;
                
	            if ($this->getRequest()->isPost()) {
	                if ($form->isValid($_POST)) {
	                    $threadId = $form->getValue("tid");                 
	                    $type = $form->getValue("messageType");
	                    $message = nl2br(trim($form->getValue("message")));
	                    $db = Zend_Registry::get("db");
	                    $db->beginTransaction();
	                    
	                    $allUsers = $this->getAllUsersInThread($loggedInUser, $threadId);
	                    $data = array(
	                       Message::TYPE => $type,
	                       Message::MESSAGE => $message,
	                       Message::CREATED => $this->_helper->generator->generateCurrentTime(),
	                       Message::USER_ID => $loggedInUser->id
	                    );
	                    $newMessageId = $this->message->addMessage($data);
	                    // update user message
	                    foreach($allUsers as $user) {
	                    	if ($loggedInUser->id != $user->id) {
	                    		$data = array(
	                               UserMessage::MESSAGE_ID => $newMessageId,
	                               UserMessage::TARGET_USER_ID => $user->id
	                            );
	                            $userMessageId = $this->userMessage->addUserMessage($data);
	                            
	                            $data = array(
	                               MessageThread::THREAD_ID => $threadId,
	                               MessageThread::USER_MESSAGE_ID => $userMessageId
	                            );
	                            $this->messageThread->addToThread($data);
	                            
	                            // log activity
			                    Activity::logActivity(
			                      Activity::SEND_USER_MESSAGE, 
			                      $loggedInUser->id,
			                      $userMessageId,
			                      $this->_helper->generator->generateCurrentTime()
			                    );
			                    // add to mail queue
			                    $extra = array(
			                        "thread" => $threadId,
			                        "message" => $message
			                    );
			                    
			                    $emailMessage = EmailTemplate::getEmailMessage(Activity::SEND_USER_MESSAGE, $loggedInUser, $user, $extra);
			                    $this->mailQueue->addToQueue(
			                      Activity::SEND_USER_MESSAGE, 
			                      EmailTemplate::SUBJECT_USER_MESSAGE, 
//			                      $loggedInUser->email,
                                  Constant::SYS_NO_REPLY_EMAIL,  
			                      $user->email, 
			                      $emailMessage, 
			                      $this->_helper->generator->generateCurrentTime());
	                    	}
	                    }
	                    $db->commit();
	                    $this->_redirect("/message/view/tid/".$threadId);
	                }
	            }
            } else {
                throw new Zend_Exception("Can not load message!");
            }
        } else {
            $this->_redirect( "/user/login?next=".urlencode($this->_helper->generator->getCurrentURI()) );
        }
    }
    
    /**
     * Get users involved in given message thread and set to session,
     * to be used in all message session.
     * 
     * @param $thread
     * @return list of users
     */
    private function getThreadUsers($thread) {
    	$users = array();
    	$userIds = array();
    	foreach (MessageThread::findByThread($thread->thread_id) as $obj) {
    		$userIds[$obj["target_user_id"]] = $obj["target_user_id"];
		}
		$usersObj = $this->user->findByUserIds($userIds);
		foreach ($usersObj as $userObj) {
			$users[$userObj->id] = $userObj;
		}
		SessionUtil::setProperty(SessionUtil::THREAD_USERS, $users);
    }
    
    /**
     * Find all root messages by given messages.
     * 
     * @param $messages the message found related to the user, either sender or recipient
     */
    private function getAllRootMessage($messages) {
    	$ret = array();
    	$index = 0;
        foreach($messages as $message) {
        	$parentMessage = Message::getParentMessage($message);
            if (!isset($parentMessage)) {
                if (!$this->hasContain($message, $ret)) {
                    $ret[$index++] = $message;
                }
            } else {
                if (!$this->hasContain($parentMessage, $ret)) {
                    $ret[$index++] = $parentMessage;
                }
            }
        }
        return $ret;
    }
    
    /**
     * Check if the given message has already in the given messages array.
     * 
     * @param $message
     * @param $messages
     * @return return true if the message already there, otherwise, return false
     */
    private function hasContain($message, $messages) {
    	$ret = false;
        foreach ($messages as $m) {
        	if ($message->id == $m->id) {
        		$ret = true;
        		break;
        	}
        }
        return $ret;
    }
    
    /**
     * Generate message object array and pass it to view render.
     * 
     * @param $userMessages
     */
    private function generateMessageDTO($messages) {
        $ret = array();
        $index = 0;
        foreach ($this->getAllRootMessage($messages) as $message) {
        	$ret[$index++] = $this->generateOneMessageDTO($message, true);
        }
        return $ret;
    }
    
    /**
     * Generate messages dto from given messages.
     * 
     * @param $messages messages from db
     * @return multitype:
     */
    private function generateMessagesDTO($messages) {
        $ret = array();
        $index = 0;
        foreach ($messages as $message) {
            $ret[$index++] = $this->generateOneMessageDTO($message, false);
        }
        return $ret;
    }
    
    /**
     * Generate one message DTO.
     * 
     * @param $message
     * @param $showLatest show latest entry or top message, default is true
     */
    private function generateOneMessageDTO($message, $showLatest=true) {
        $messageDTO = new MessageDTO();
        $messageToShow = null;
        if ($showLatest) {
        	// get reply message, order by created desc
        	$replyMsgs = Message::getReplyMessages($message);
        	
        	if (empty($replyMsgs) || count($replyMsgs) < 1) {
        		$messageToShow = $message;
        	} else {
        		$latestMessage = $replyMsgs[count($replyMsgs)-1];
        		$messageToShow = $latestMessage;
        	}
        } else {
        	$messageToShow = $message;
        }
        $messageDTO->messageId = $message->id;
        $messageDTO->sendUser = Message::getUser($message);
        $messageDTO->subject = $message->subject;
        $messageDTO->created = $message->created;
        $messageDTO->message = $messageToShow->message;
        $messageDTO->targetUsers = Message::getUserMessageTargetUser($message);
        $messageDTO->replyMessages = $this->generateMessagesDTO(Message::getReplyMessages($message));
        $messageDTO->read = $this->getMessagesReadStatus($message, Message::getReplyMessages($message));
        
        return $messageDTO;
    }
    
    /**
     * Get message read status.
     * 
     * @param $message
     * @param $replyMessages
     * @return return trun if all of message and replyMessages are marked as read
     */
    private function getMessagesReadStatus($message, $replyMessages) {
    	$read = true;
    	if (Message::getUserMessage($message)->status == UserMessageStatus::UNREAD) {
    		return false;
    	}
    	foreach ($replyMessages as $msg) {
    		if (Message::getUserMessage($msg)->status == UserMessageStatus::UNREAD) {
    			return false;
    		}
    	}
    	return $read;
    }
    
    /**
     * 
     * @param $type inbox or sent
     * @param $messageThreads message thread from db
     */
    private function generateMessageThreadDTO($threads, $loggedInUser, $type) {
    	
    	$messageThreads = array();
        foreach ($threads as $thread) {
            $messageThreadDTO = $this->generateSingleMessageThreadDTO($thread, $loggedInUser, $type, false);
            $messageThreads["$thread->thread_id"] = $messageThreadDTO;
        }
        return $messageThreads;
    }
    
    /**
     * Generate single message thread DTO from thread from db.
     * 
     * @param $thread
     * @param $loggedInUser
     * @param $type inbox or sent
     * @param $loadUserMessages if it should load user message or not
     * @return MessageThreadDTO
     */
    private function generateSingleMessageThreadDTO($thread, $loggedInUser, $type, $loadUserMessages=false) {
    	$messageThreadDTO = new MessageThreadDTO();
        $messageThreadDTO->threadId = $thread->thread_id;
        $messageThreadDTO->subject = $this->messageThread->getSubject($thread->thread_id);
        
        if ($loadUserMessages) {
        	$messageThreadDTO->userMessages = $this->generateUserMessages($thread->thread_id);
        }
        $info = array();
        if ($type == "inbox") {
            $info = MessageThread::findLastReplyInfoInThread($loggedInUser, $thread->thread_id);
        } else if ($type == "sent") {
            $info = MessageThread::findLastSentInfoInThread($loggedInUser, $thread->thread_id);
        } else {
            $info = MessageThread::findLastReplyInfoInThread($loggedInUser, $thread->thread_id);
        }
        $messageThreadDTO->displayUser = $this->user->findById($info["uid"]);
        $messageThreadDTO->lastMessageCreated = $info["created"];
        $messageThreadDTO->displayMessage  = $info["message"];
        $messageThreadDTO->read = MessageThread::isReadMessageThread($loggedInUser, $thread->thread_id);
        return $messageThreadDTO;    
    }
    
    /**
     * Generate all user messages in given message thread.
     * 
     * @param $threadId
     */
    private function generateUserMessages($threadId) {
    	$result = array();
    	$userMessageIds = MessageThread::findByThread($threadId);
    	$involvedUsers = SessionUtil::getProperty(SessionUtil::THREAD_USERS);
    	foreach ($userMessageIds as $userMessageId) {
//    		$userMessage = $this->userMessage->findById($userMessageId["user_message_id"]);
    		
    		$userMessageDTO = new UserMessageDTO();
    		$userMessageDTO->userMessageId = $userMessageId["user_message_id"];
    		$userMessageDTO->targetUser = $involvedUsers[$userMessageId["target_user_id"]];
    		$userMessageDTO->messageId = $userMessageId["message_id"];
    		$userMessageDTO->message = $userMessageId["message"];
    		$userMessageDTO->creator = $involvedUsers[$userMessageId["creator"]];
    		$userMessageDTO->created = $userMessageId["created"];
    		$userMessageDTO->status = $userMessageId["status"];
    		$result[$userMessageId["user_message_id"]] = $userMessageDTO;
    	}
    	return $result;
    }
    
//    /**
//     * Reply message action.
//     */
//    public function replymessageAction() {
//        if ($this->_helper->user->isLoggedIn()) {
//        	$loggedInUser = $this->user->findById($this->_helper->user->getUserData()->id);
//        	$messageId = $this->_getParam("mid");
//        	$message = $this->message->findById($messageId);
//            $form = new ReplyMessageForm($message);
//            $this->view->form = $form;
//            
//        } else {
//            $this->_redirect("/user/login");
//        }
//    }
    
    /**
     * Send mails from mail_queue periodically.
     * First, need to have a security check that only allow 
     * to run the script when ctl and corresponding crc matches.
     * Second, collect all emails that need to be sent out,
     * criteria can be send only emails marked as SENT 'false',
     * can also given timestamp, by default, it only send the mails
     * created the same day.
     * 
     * Example: ctl 1686825444, crc e233847ad0507ae1d0890633ced692b9
     */
    public function sendemailAction() {
        $this->_helper->viewRenderer->setNoRender();   //view info disabled
        $this->_helper->layout->disableLayout();
        //$body, $recipients, $from, $subject
        $ctl = $this->_getParam('ctl');
        $crc = $this->_getParam('crc');
        $customerKey = '';
        $startDate = $this->_getParam('start');
        
        $generated_crc = md5($ctl);
        foreach ($this->mailQueue->findUnsentMails() as $mail) {
            if ($generated_crc == $crc) {
                $from = $mail->sender;
                $failMessage = Constant::EMAIL_FAIL_MESSAGE;
                $db = Zend_Registry::get('db');
                $db->beginTransaction();
                if (!empty($mail->message)) {
                    $failMessage = $this->_helper->swiftEmail->sendEmail($mail->message, $mail->recipients, $mail->sender, $mail->subject);
                }
                $this->mailQueue->updateStatus($mail->id, $failMessage, $this->_helper->generator->generateCurrentTime());
                $db->commit();
            } else {
                throw new Exception('Not allowed to run the service!');
            }
        }
    }
    
    /**
     * Generate unique message thread id.
     * 
     * @return return message thread id string
     */
    private function generateThreadId() {
        $threadId = $this->_helper->generator(32, 'alpha');
        $obj = $this->messageThread->findByThreadId($threadId);
        $maxTries = 5;
        $try = 1;
        while (isset($obj) && $try < $maxTries) {
            $threadId = $this->_helper->generator(32, 'alpha');
            $obj = $this->messageThread->findByThreadId($threadId);
            $try ++;
        }
        $obj = $this->messageThread->findByThreadId($threadId);
        if (isset($obj)) {
            throw new Zend_Exception("Can't generate unique message thread id");
        }
        return $threadId;
    }
    
    /**
     * Get users that in the given message thread, exclude given user if any.
     * 
     * @param $excludeUser
     * @param $threadId
     * @return return array of users
     */
    private function getAllUsersInThread($excludeUser, $threadId) {
    	$users = array();
    	$userMessages = $this->generateUserMessages($threadId);
    	foreach ($userMessages as $userMessage) {
    		$user = $userMessage->targetUser;
    		if ($user->id != $excludeUser->id) {
    			$users[$user->id] = $user;
    		}
    		$user = $userMessage->creator;
    		if ($user->id != $excludeUser->id) {
    			$users[$user->id] = $user;
    		}
    	}
    	return $users;
    }
}

